home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD 2.1 / Amiga Developer CD v2.1.iso / Reference / Amiga_Mail_Vol2 / Archives / Plain / jf91 / amcx.txt next >
Encoding:
Text File  |  1991-04-15  |  47.2 KB  |  1,167 lines

  1. (c)  Copyright 1991 Commodore-Amiga, Inc.   All rights reserved.
  2. The information contained herein is subject to change without notice,
  3. and is provided "as is" without warranty of any kind, either expressed
  4. or implied.  The entire risk as to the use of this information is
  5. assumed by the user.
  6.  
  7.  
  8.  
  9.  
  10. Introduction to Commodities Exchange
  11.  
  12.  
  13. by John Orr
  14.  
  15. The input.device has a hand in almost all user input on the Amiga.  It
  16. gathers input events from the keyboard, the gameport (mouse), and
  17. several other sources, into one input ``stream''.  Special programs
  18. called input event handlers intercept input events
  19.  along this stream, examining and sometimes changing the input events.
  20. Both Intuition and the console device use input handlers to process
  21. user input.
  22.  
  23. Using the input.device, a program can introduce its own custom handler
  24. into the chain of input handlers at almost any point in the chain.
  25. ``Hot key'' programs, shell pop-up programs, and screen blankers all
  26. commonly use custom input handlers to monitor u ser input before it
  27. gets to the Intuition input handler.
  28.  
  29. Custom input handlers do have their drawbacks, however.  Not only are
  30. these handlers hard to program, but because there is no standard way
  31. to implement and control them, multiple handlers often do not work
  32. well together.  Their antisocial behavior can res ult in load order
  33. dependencies and incompatibilities between different custom input
  34. handlers.  Even for the expert user, having several custom input
  35. handlers coexist peacefully can be next to impossible.  The custom
  36. input handler needs to take its next ev olutionary step.
  37.  
  38. Commodities Exchange is that step.  It provides a simple, standardized
  39. way to program and control custom input handlers.  It is divided into
  40. three parts: an Exec library, a controller program, and a scanned
  41. library.
  42.  
  43.  
  44.  
  45. The Exec library is called commodities.library.  When it is first
  46. opened, commodities.library establishes a single input handler just
  47. before Intuition in the input chain.  When this input handler receives
  48. an input event, it creates a CxMessage (Commoditie s Exchange Message)
  49. corresponding to the input event, and diverts the CxMessage through
  50. the network of Commodities Exchange input handlers.  These handlers
  51. are made up of trees of different CxObjects (Commodities Exchange
  52. Objects), each of which performs a simple operation on the CxMessages.
  53. Any CxMessages that exit the network are returned to the
  54. input.device's input stream as input events.
  55.  
  56. Through function calls to the commodities.library, an application can
  57. install a custom input handler.  A Commodities Exchange application,
  58. sometimes simply referred to as a commodity, uses the CxObject
  59. primitives to do things such as filter certain CxMess ages, translate
  60. CxMessages, signal a task when a CxObject receives a CxMessage, send a
  61. message when a CxObject receives a CxMessage, or if necessary, call a
  62. custom function when a CxObject receives a CxMessage.
  63.  
  64. The controller program is called Commodities Exchange.  The user can
  65. monitor and control all the currently running Commodities Exchange
  66. applications from this one program.  The user can enable and disable a
  67. commodity, kill a commodity, or, if the commodit y has a window, ask
  68. the commodity to show or hide its window.  When the user requests any
  69. of these actions, the controller program sends the commodity a
  70. message, telling it which action to perform.
  71.  
  72. The third component of Commodities Exchange is its scanned library
  73. functions.  These functions are part of the amiga.lib scanned library.
  74. They do a lot of the work involved with parsing command lines and tool
  75. types.
  76.  
  77. Commodities Exchange is ideal for programs like hot keys/pop ups,
  78. screen blankers, and mouse blankers that need to monitor all user
  79. input.  Commodities Exchange should not be used as an alternate method
  80. of receiving user input for an application.  Other a pplications
  81. depend on getting user input in some form or another from the input
  82. stream.  A greedy program that diverts input to itself rather than
  83. letting the input go to where the user expects it can seriously
  84. confuse the user, not to mention compromise
  85.  the advantages of multitasking.
  86.  
  87.  
  88.  
  89. CxObjects
  90.  
  91. CxObjects are the basic building blocks used to construct a commodity.
  92. A commodity uses CxObjects to take care of all manipulations of
  93. CxMessages.  When a CxMessage ``arrives'' at a CxObject, that CxObject
  94. carries out its primitive action and then, if it
  95.  has not deleted the CxMessage, it passes the CxMessage on to the next
  96. CxObject.  A commodity links together CxObjects into a tree,
  97. organizing these simple action objects to perform some higher
  98. function.
  99.  
  100. A CxObject is in one of two states, active or inactive.  An active
  101. CxObject performs its primitive action every time it receives a
  102. CxMessage.  If a CxObject is inactive, CxMessages bypass it,
  103. continuing to the CxObject that follows the inactive one.  By d
  104. efault, all CxObjects except the type called brokers are created in
  105. the active state.
  106.  
  107.  
  108.  
  109. Installing a Broker
  110.  
  111. The Commodities Exchange input handler maintains a master list of
  112. CxObjects to which it diverts input events using CxMessages.  The
  113. CxObjects in this master list are a special type of CxObject called a
  114. broker.  The only thing a broker CxObject does is div ert CxMessages
  115. to its own personal list of CxObjects.  A commodity creates a broker
  116. and attaches other CxObjects to it.  These attached objects take care
  117. of the actual input handler related work of the commodity and make up
  118. the broker's personal list.
  119.  
  120. Program listing 1, Broker.c, (located at the end of this article) is a
  121. very simple example of a working commodity.  It serves only to
  122. illustrate the basics of a commodity, not to actually perform any
  123. useful function.  It shows how to set up a broker and p rocess
  124. commands from the controller program.
  125.  
  126. Besides opening commodities.library and creating an Exec message port,
  127. setting up a commodity requires creating a broker.  The function
  128. CxBroker() creates a broker and adds it to the master list.
  129.  
  130. CxObj *CxBroker(struct NewBroker *nb, long *error);
  131.  
  132. CxBroker()'s first argument is a pointer to a NewBroker structure:
  133.  
  134. struct NewBroker {
  135.    BYTE     nb_Version;  /* There is an implicit pad byte after this BYTE */
  136.    BYTE     *nb_Name;
  137.    BYTE     *nb_Title;
  138.    BYTE     *nb_Descr;
  139.    SHORT    nb_Unique;
  140.    SHORT    nb_Flags;
  141.    BYTE     nb_Pri;      /* There is an implicit pad byte after this BYTE */
  142.    struct   MsgPort   *nb_Port;
  143.    WORD     nb_ReservedChannel;
  144. };
  145.  
  146. Commodities Exchange gets all the information it needs about the
  147. broker from this structure.  NewBroker's nb_Version field contains the
  148. version number of the NewBroker structure.  This should be set to
  149. NB_VERSION which is defined in <libraries/commodities .h>.  The
  150. nb_Name, nb_Title, and nb_Descr point to strings which hold the name,
  151. title, and description of the broker.  The two bit fields, nb_Unique
  152. and nb_Flags, toggle certain features of Commodities Exchange based on
  153. their values.  They are discussed in detail later in this article.
  154.  
  155. The nb_Pri field contains the broker's priority.  Commodities Exchange
  156. inserts the broker into the master list based on this number.  Higher
  157. priority brokers get CxMessages before lower priority brokers.
  158.  
  159. CxBroker()'s second argument is a pointer to a LONG.  If this pointer
  160. is not NULL, CxBroker() fills in this field with one of the following
  161. codes from <libraries/commodities.h>:
  162.  
  163. CBERR_OK        0        /* No error                         */
  164. CBERR_SYSERR    1        /* System error , no memory, etc    */
  165. CBERR_DUP       2        /* uniqueness violation             */
  166. CBERR_VERSION   3        /* didn't understand nb_VERSION     */
  167.  
  168.  
  169. CxMessages
  170.  
  171. There are actually two types of CxMessages.  The first, CXM_IEVENT,
  172. corresponds to an input event and travels through the Commodities
  173. Exchange network.  The other type, CXM_COMMAND, carries a command to a
  174. commodity.  A CXM_COMMAND normally comes from the controller program
  175. and is used to pass user commands on to a commodity.  A commodity
  176. receives these commands through an Exec message port that the
  177. commodity sets up before it calls CxBroker.  The NewBroker's nb_Port
  178. field points to this message port.  A c ommodity can tell the
  179. difference between the two types of CxMessages by calling the
  180. CxMsgType() function.
  181.  
  182. ULONG CxMsgType(CxMsg *cxm);
  183. UBYTE *CxMsgData(CxMsg *cxm);
  184. LONG  CxMsgID(CxMsg *cxm);
  185.  
  186. A CxMessage not only has a type, it can also have a data pointer as
  187. well as an ID associated with it.  The data associated with a
  188. CXM_IEVENT is an InputEvent structure.  By using the CxMsgData()
  189. function, a commodity can obtain a pointer to the correspond ing
  190. InputEvent of a CXM_IEVENT.  Commodities Exchange does not give an ID
  191. to the CXM_IEVENT CxMessages it introduces to the Commodities network,
  192. but certain CxObjects can assign an ID to a CXM_IEVENT CxMessage.
  193.  
  194. CXM_COMMAND CxMessages generally do not use the data pointer.
  195. Instead, they use an ID.  They use this ID to specify the command
  196. passed to a commodity.  CxMsgID() extracts the ID from a CxMessage.
  197.  
  198.  
  199. oldactivationvalue = LONG ActivateCxObj(CxObj *co, long newactivationvalue);
  200.  
  201.  
  202. After successfully completing the initial set up and activating the
  203. broker using ActivateCxObj(), a commodity can begin its input
  204. processing loop.  The example Broker.c receives input from one source,
  205. the controller program.  The controller program sends a CxMessage each
  206. time the user clicks its enable, disable, or kill gadgets.  Using the
  207. CxMsgID() function, the commodity finds out what the command is and
  208. executes it.
  209.  
  210. Notice that Broker.c uses Ctrl-C as a break key.  This is a change
  211. from 1990 Atlanta DevCon Notes on Commodities Exchange which said to
  212. use Ctrl-E.  The break key for any commodity should be Ctrl-C.
  213.  
  214. The commands that a commodity can receive from the controller program
  215. (as defined in <libraries/commodities.h>) are:
  216.  
  217. CXCMD_DISABLE     /* please disable yourself       */
  218. CXCMD_ENABLE      /* please enable yourself        */
  219. CXCMD_KILL        /* go away for good              */
  220. CXCMD_APPEAR      /* open your window, if you can  */
  221. CXCMD_DISAPPEAR   /* hide your window              */
  222.  
  223. The CXCMD_DISABLE, CXCMD_ENABLE, and CXCMD_KILL commands correspond to
  224. the similarly named controller program gadgets, ``Disable'',
  225. ``Enable'', and ``Kill''; CXCMD_APPEAR and CXCMD_DISAPPEAR correspond
  226. to the controller program gadgets, ``Show'' and ``Hi de''.  These
  227. gadgets are ghosted in Broker.c because it has no window (It doesn't
  228. make much sense to give the user a chance to click the ``Show'' and
  229. ``Hide'' gadgets).  In order to do this, Broker.c has to tell
  230. Commodities Exchange to ghost these gadge ts.  When CxBroker() sets up
  231. a broker, it looks at the NewBroker.nb_Flags field to see if the
  232. COF_SHOW_HIDE bit (from <libraries/commodities.h>) is set.  If it is,
  233. the ``Show'' and ``Hide'' gadgets for this broker will be selectable.
  234. Otherwise they are g hosted and disabled.
  235.  
  236. Shutting down a commodity is easy.  After replying to all CxMessages
  237. waiting at the broker's message port, a commodity can delete its
  238. CxObjects.  DeleteCxObj() removes a single CxObject from the
  239. Commodities network.
  240.  
  241. void DeleteCxObj(CxObj *co);
  242.  
  243.  
  244. Tool Types
  245.  
  246. A goal of Commodities Exchange is to improve user control over input
  247. handlers.  One way in which it accomplishes this is through the use of
  248. standard ToolTypes.  The user will expect commodities to recognize the
  249. set of standard tool types:
  250.  
  251. CX_PRIORITY
  252. CX_POPUP
  253. CX_POPKEY
  254.  
  255. CX_PRIORITY lets the user set the priority of a commodity.  The string
  256. "CX_PRIORITY=" is a number from -128 to 127.  The higher the number,
  257. the higher the priority of the commodity, giving it access to input
  258. events before lower priority commodities.  All commodities should
  259. recognize CX_PRIORITY.
  260.  
  261. CX_POPUP and CX_POPKEY are only relevant to commodities with a window.
  262. The string "CX_POPUP=" should be followed by a "yes" or "no", telling
  263. the commodity if it should or shouldn't show its window when it is
  264. first launched.  CX_POPKEY is followed by a st ring describing the key
  265. to use as a hot key for ``popping up'' the commodity's window.  The
  266. description string for CX_POPKEY describes an input event.  The
  267. specific format of the string is discussed later in this article.
  268.  
  269. Commodities Exchange's support library functions simplify parsing
  270. arguments from either the Workbench or a CLI.  A Workbench launched
  271. commodity gets its arguments directly from the tool types in the
  272. commodity's icon.  CLI launched commodities get their ar guments from
  273. the command line, but these arguments look exactly like the tool types
  274. from the commodity's icon.  For example, the following command line
  275. sets the priority of a commodity called HotKey to 5:
  276.  
  277. HotKey "CX_PRIORITY=5"
  278.  
  279. Commodities Exchange has several support library functions used to
  280. parse arguments:
  281.  
  282. tooltypearray = char **ArgArrayInit(int argc, char **argv);
  283. void ArgArrayDone(void);
  284. tooltypevalue = char *ArgString(char **tooltypearray,
  285.                                 char *tooltype, char *defaultvalue);
  286. tooltypevalue = LONG *ArgInt(char **tooltypearray,
  287.                              char *tooltype, LONG defaultvalue);
  288.  
  289. ArgArrayInit() initializes a tool type array of strings which it
  290. creates from the startup arguments, argc and argv.  It doesn't matter
  291. if these startup arguments come from the Workbench or from a CLI,
  292. ArgArrayInit() can extract arguments from either source.  Because
  293. ArgArrayInit() uses some icon.library functions, a commodity is
  294. responsible for opening that library before using the function.
  295.  
  296. ArgArrayInit() also uses some resources that must be returned to the
  297. system when the commodity is done.  ArgArrayDone() performs this clean
  298. up.  Like ArgArrayInit(), ArgArrayDone() uses icon.library, so the
  299. library has to remain open until ArgArrayDone() is finished.
  300.  
  301. The support library has two functions that use the tool type array set
  302. up by ArgArrayInit(), ArgString() and ArgInt().  ArgString() scans the
  303. tool type array for a specific tool type.  If successful, it returns a
  304. pointer to the value associated with that tool type.  If it doesn't
  305. find the tool type, it returns the default value passed to it.
  306. ArgInt() is similar to ArgString().  It also scans the
  307. ArgArrayInit()'s tool type array, but it returns a LONG rather than a
  308. string pointer.  ArgInt() extracts the i nteger value associated with
  309. a tool type, or, if that tool type is not present, it returns the
  310. default value.
  311.  
  312. Of course, these tool type parsing functions are not restricted to the
  313. standard Commodities Exchange tool types.  A commodity that requires
  314. any arguments should use these functions along with custom tool types
  315. to obtain these values.  Because the Commodit ies Exchange standard
  316. arguments are processed as tool types, the user will expect to enter
  317. other arguments as tool types.
  318.  
  319.  
  320. Filter CxObjects
  321.  
  322. Because not all commodities are interested in every input event that
  323. makes it way down the input chain, Commodities Exchange has a method
  324. for filtering them.  A filter CxObject compares the CxMessages it
  325. receives to a pattern.  If a CxMessage matches the pattern, the filter
  326. diverts the CxMessage down its personal list of CxObjects.
  327.  
  328. CxObj *CxFilter(char *descriptionstring);
  329.  
  330. The C macro CxFilter() (defined in <libraries/commodities.h>) returns
  331. a pointer to a filter CxObject.  The macro has only one argument, a
  332. pointer to a string describing which input events to filter.  The
  333. following regular expression outlines the format of a description
  334. string (CX_POPKEY uses the same description string format):
  335.  
  336.  
  337. [class]   ( [-] (qual | syn) )*   [ [-] upstroke]   [highmap |ANSICode]
  338.                  the * means zero or more occurances of ( [-] ( qual | syn ) )
  339.  
  340.  
  341.  
  342. where:
  343.  
  344.  
  345. class can be any one of the strings in the table below.  Each string
  346. corresponds to a class of input event (shown below).  The classes are
  347. defined in <devices/inputevent.h>.  Commodities Exchange will assume
  348. the class is rawkey if the class is not explici tly stated.
  349.  
  350. "rawkey"        IECLASS_RAWKEY
  351. "rawmouse"      IECLASS_RAWMOUSE
  352. "event"         IECLASS_EVENT
  353. "pointerpos"    IECLASS_POINTERPOS
  354. "timer"         IECLASS_TIMER
  355. "newprefs"      IECLASS_NEWPREFS
  356. "diskremoved"   IECLASS_DISKREMOVED
  357. "diskinserted"  IECLASS_DISKINSERTED
  358.  
  359.  
  360. qual is one of the strings in the table below.  Each string
  361. corresponds to an input event qualifier (followed by its ID from
  362. <devices/inputevent.h>).  A dash preceding the qualifier string tells
  363. the filter object not to care if that qualifier is present i n the
  364. input event.  Notice that there can be more than one qual (or none at
  365. all) in the input description string.
  366.  
  367. "lshift"            IEQUALIFIER_LSHIFT
  368. "rshift"            IEQUALIFIER_RSHIFT
  369. "capslock"          IEQUALIFIER_CAPSLOCK
  370. "control"           IEQUALIFIER_CONTROL
  371. "lalt"              IEQUALIFIER_LALT
  372. "ralt"              IEQUALIFIER_RALT
  373. "lcommand"          IEQUALIFIER_LCOMMAND
  374. "rcommand"          IEQUALIFIER_RCOMMAND
  375. "numericpad"        IEQUALIFIER_NUMERICPAD
  376. "repeat"            IEQUALIFIER_REPEAT
  377. "midbutton"         IEQUALIFIER_MIDBUTTON
  378. "rbutton"           IEQUALIFIER_RBUTTON
  379. "leftbutton"        IEQUALIFIER_LEFTBUTTON
  380. "relativemouse"     IEQUALIFIER_RELATIVEMOUSE
  381.  
  382.  
  383.  
  384. syn is one of the strings in the table below.  These strings act as
  385. synonyms for groups of qualifiers.  Each string below is followed by
  386. its identifier from <libraries/commodities.h>.  A dash preceding the
  387. synonym string tells the filter object not to car e if that qualifier
  388. is present in the input event.  Notice that there can be more than one
  389. syn (or none at all) in the input description string.
  390.  
  391. "shift"     IXSYM_SHIFT    /* look for either shift key */
  392. "caps"      IXSYM_CAPS     /* look for either shift key or capslock */
  393. "alt"       IXSYM_ALT      /* look for either alt key */
  394.  
  395.  
  396.  
  397. upstroke is the literal string "upstroke".  If this string is absent,
  398. the filter considers only downstrokes.  If is present alone, the
  399. filter considers only upstrokes.  If preceded by a dash, the filter
  400. considers both upstrokes and downstrokes.
  401.  
  402.  
  403. highmap is one of the following strings:
  404. "space", "backspace", "tab", "enter", "return", "esc", "del", "up",
  405. "down", "right", "left", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
  406. "f8", "f9", "f10", "help".
  407.  
  408.  
  409. ANSICode is a single character (for example `a`) that Commodities
  410. Exchange looks up in the system default keymap.
  411.  
  412.  
  413. The following are some example description strings:
  414.  
  415. ``rawkey lshift alt f2''    Function key f2 with the left shift and
  416.                               either alt key pressed.
  417. ``-shift -alt -control a''  The key that produces an 'a' with or
  418.                               without any shift, alt, or control keys
  419.                               pressed.
  420. ``rawmouse rbutton''        A mouse move with the right mouse button down.
  421. ``timer''                   A timer event.
  422.  
  423.  
  424. As of commodities.library version 37.3, ParseIX(), the function used
  425. to parse input description strings, does not parse certain classes
  426. correctly.  Only the rawkey, diskremoved, and newprefs classes are
  427. properly parsed.  For the moment, any commodity that
  428.  needs to look for the other classes must directly use Commodities
  429. Exchange's internal format for input event descriptions, input
  430. expressions or IX, discussed later in this article.
  431.  
  432. A filter has to be inserted into the Commodities network before it can
  433. process any CxMessages.  AttachCxObj() adds a CxObject to the personal
  434. list of another commodity.  The HotKey.c example uses this function to
  435. attach its filter to a broker.
  436.  
  437. void AttachCxObj(CxObj *HeadObj, CxObj *co);
  438.  
  439. This function uses the Exec function AddTail() to add the CxObject to
  440. the end of HeadObj's personal list.  The position of a CxObject list
  441. determines which CxObject gets CxMessages first.  Other functions
  442. exist to add CxObjects at different points in the list (see the
  443. commodities.doc Autodoc for details).
  444.  
  445.  
  446. Senders CxObjects
  447.  
  448. A filter CxObject by itself is not especially useful.  It needs some
  449. other CxObjects attached to it.  A commodity interested in knowing if
  450. a specific key was pressed uses a filter to detect and divert the
  451. corresponding CxMessage down the filter's personal
  452.  list.  The filter does this without letting the commodity know what
  453. happened.  The sender CxObject is used to notify a commodity that it
  454. received a CxMessage.  CxSender() is a macro that creates a sender
  455. CxObject.
  456.  
  457. senderCxObj = CxObj *CxSender(struct MsgPort *senderport, LONG cxmID);
  458.  
  459. CxSender() supplies the sender with an Exec message port and an ID.
  460. For every CxMessage a sender receives, it sends a new CxMessage to the
  461. Exec message port passed in CxSender().  Normally, the commodity
  462. creates this port.  It is not unusual for a commod ity's broker and
  463. sender(s) to share an Exec message port.  The HotKey.c example does
  464. this to avoid creating unnecessary message ports.  A sender uses the
  465. ID (cxmID) passed to CxSender() as the ID for all the CxMessages that
  466. the it transmits.  A commodity uses the ID to monitor CxMessages from
  467. several senders at a single message port.
  468.  
  469. A sender does several things when it receives a CxMessage.  First, it
  470. duplicates the CxMessage's corresponding input event and creates a new
  471. CxMessage.  Then, it points the new CxMessage's data field to the copy
  472. of the input event and sets the new CxMessa ge's ID to the ID passed
  473. to CxSender().  Finally, it sends the new CxMessage to the port passed
  474. to CxSender(), asynchronously.
  475.  
  476. Because HotKey uses only one message port between its broker and
  477. sender object, it has to extract the CxMessage's type so it can tell
  478. if it is a CXM_IEVENT or a CXM_COMMAND.  If HotKey gets a CXM_IEVENT,
  479. it compares the CxMessage's ID to the sender's ID, EVT_HOTKEY, to see
  480. which sender sent the CxMessage.  Of course HotKey has only one
  481. sender, so it only checks for only one ID.  If it had more senders,
  482. HotKey would check for the ID of each of the other senders as well.
  483.  
  484. Although HotKey doesn't use it, a CXM_IEVENT CxMessage contains a
  485. pointer to the copy of an input event.  A commodity can extract this
  486. pointer (using CxMsgData() ) if it needs to examine the input event
  487. copy.  This pointer is only valid before the CxMessa ge reply.  Note
  488. that it does not make any sense to modify the input event copy.
  489.  
  490. Senders are attached almost exclusively to CxObjects that filter out
  491. most input events (usually a filter CxObject).  Because a sender sends
  492. a CxMessage for every single input event it gets, it should only get a
  493. select few input events.  The AttachCxObj() function can add a
  494. CxObject to the end of a filter's (or some other filtering CxObject's)
  495. personal list.  A commodity should not attach a CxObject to a sender
  496. as a sender ignores any CxObjects in its personal list.
  497.  
  498.  
  499. Translate CxObjects
  500.  
  501. Normally, after a commodity processes a hot key input event, it needs
  502. to eliminate the input event.  Other commodities may need to replace
  503. an input event with a different one.  The translate CxObject can be
  504. used for these purposes.
  505.  
  506. translateCxObj = CxObj *CxTranslate(struct InputEvent *newinputevent);
  507.  
  508. The macro CxTranslate() creates a new translate CxObject.
  509. CxTranslate()'s only argument is a pointer to a chain of one or more
  510. InputEvent structures.  When a translate CxObject receives a
  511. CxMessage, it eliminates the CxMessage and its corresponding input
  512. event from the system.  The translator introduces a new input event,
  513. which Commodities Exchange copies from the InputEvent structure passed
  514. to CxTranlate() (newinputevent from the function prototype above), in
  515. place of the deleted input event.  The trans lator introduces the new
  516. input event after the Commodities Exchange input handler in the
  517. input.device's input stream.  Any CxObjects that follow the translator
  518. in the Commodities network will see neither the deleted CxMessage nor
  519. the new input event.
  520.  
  521. A translator is normally attached to some kind of filtering CxObject.
  522. If it wasn't, it would translate all input events into the same exact
  523. input event.  Like the sender CxObject, a translator does not divert
  524. CxMessages down its personal list, so it does n't serve any purpose to
  525. add any to it.
  526.  
  527. HotKey utilizes a special kind of translator.  Instead of supplying a
  528. new input event, HotKey passes a NULL to CxTranslate().  If a
  529. translator has a NULL new input event pointer, it does not introduce a
  530. new input event, but still eliminates any CxMessages and corresponding
  531. input events it receives.
  532.  
  533.  
  534.  
  535. CxObject Errors
  536.  
  537. A Commodities Exchange function that acts on a CxObject records errors
  538. in the CxObject's accumulated error field.  The function CxObjError()
  539. returns a CxObject's error field.
  540.  
  541. co_errorfield = LONG CxObjError( CxObj *co );
  542.  
  543. Each bit in the error field corresponds to a specific type of error.
  544. The following is a list of the currently defined CxObject errors and
  545. their corresponding bit mask constants.
  546.  
  547. COERR_ISNULL              CxObjError() was passed a NULL.
  548. COERR_NULLATTACH          Someone tried to attach a NULL CxObj to this
  549. CxObj.  COERR_BADFILTER   This CxObj is a filter and currently has an
  550.                             invalid filter description.
  551. COERR_BADTYPE             Someone tried to perform a type specific
  552.                             function on the wrong kind of CxObject
  553.                             (for example calling SetFilter() on a
  554.                             sender CxObject).
  555.  
  556. The remaining bits are reserved by Commodore for future use.  HotKey
  557. checks the error field of its filter CxObject to make sure the filter
  558. is valid.  HotKey does not need to check the other objects with
  559. CxObjError() because it already makes sure that thes e other objects
  560. are not NULL, which is the only other kind of error the other objects
  561. can cause in this situation.
  562.  
  563.  
  564. Uniqueness
  565.  
  566. When a commodity opens its broker, it can ask Commodities Exchange not
  567. to launch another broker with the same name (nb_Name).  This
  568. uniqueness feature's purpose is to prevent the user from starting
  569. duplicate commodities.  If a commodity asks, Commodities Exchange will
  570. not only refuse to create a new, similarly named broker, but it will
  571. also notify the original commodity if someone tries to do so.
  572.  
  573. A commodity tells Commodities Exchange not to allow duplicates by
  574. setting certain bits in the nb_Unique field of the NewBroker structure
  575. it sends to CxBroker():
  576.  
  577. NBU_UNIQUE      bit 0
  578. NBU_NOTIFY      bit 1
  579.  
  580. Setting the NBU_UNIQUE bit prevents duplicate commodities.  Setting
  581. the NBU_NOTIFY bit tells Commodities Exchange to notify a commodity if
  582. an attempt was made to launch a duplicate.  Such a commodity will
  583. receive a CXM_COMMAND CxMessage with an ID of CXCM D_UNIQUE when
  584. someone tries to duplicate it.  Because the uniqueness feature uses
  585. the name a programmer gives a commodity to differentiate it from other
  586. commodities, it is possible for completely different commodities to
  587. share the same name, preventing th e two from coexisting.  For this
  588. reason, a commodity should not use a name that is likely to be in use
  589. by other commodities (like ``filter'' or ``hotkey'').
  590.  
  591. When HotKey gets a CXCMD_UNIQUE CxMessage, it shuts itself down.
  592. HotKey and all the windowless commodities that come with the 2.0
  593. Workbench shut themselves down when they get a CXCMD_UNIQUE CxMessage.
  594. Because the user will expect all windowless commodit ies to work this
  595. way, all windowless commodities should follow this standard.
  596.  
  597. When the user tries to launch a duplicate of a system commodity that
  598. has a window, the system commodity moves its window to the front of
  599. the display, as if the user had clicked the ``Show'' gadget in the
  600. controller program's window.  A windowed commodity should mimic
  601. conventions set by existing windowed system commodities, and move its
  602. window to the front of the display.
  603.  
  604.  
  605. DeleteCxObjAll()
  606.  
  607. Cleaning up after a commodity requires deleting its CxObjects.  If a
  608. commodity has a lot of CxObjects, deleting each individually can be a
  609. bit tedious.  DeleteCxObjAll() will delete a CxObject and any other
  610. CxObjects that are attached to it.
  611.  
  612. void DeleteCxObjAll( CxObj *delete_co );
  613.  
  614. HotKey uses this function to delete all its CxObjects.  A commodity
  615. that uses DeleteCxObjAll() to delete all its CxObjects should make
  616. sure that they are all connected to the main one.
  617.  
  618.  
  619. Other CxObjects
  620.  
  621. There are three remaining CxObjects:
  622.  
  623. signalCxObj = CxObj  *CxSignal(struct Task *,LONG signal);
  624. debugCxObj  = CxObj  *CxDebug(LONG ID);
  625. customCxObj = CxObj  *CxCustom(LONG *customfunction(), LONG  cxmID);
  626.  
  627. When a signal CxObject receives a CxMessage, it signals a task.  When
  628. a debug CxObject receives a CxMessage, it sends debugging information
  629. to the serial port using kprintf().  Note that the debug CxObj did not
  630. work before V37.
  631.  
  632. A custom CxObject is the only means by which a commodity can directly
  633. modify input events as they pass through the Commodities network as
  634. CxMessages.  The custom CxObject is probably the most dangerous of the
  635. CxObjects to use.  Unlike the rest of the code
  636.  a commodities programmer writes, the code passed to a custom CxObject
  637. runs as part of the input.device task, putting severe restrictions on
  638. the function.  No DOS or Intuition functions can be called.  No
  639. assumptions can be made about the values of regist ers upon entry.
  640. Any function passed to CxCustom() should be very quick and very
  641. simple, with a minimum of stack usage.
  642.  
  643. Commodities Exchange calls a custom CxObject's function as follows:
  644.  
  645. void customfunction(CxMsg *cxm, CxObj *customcxobj);
  646.  
  647. where cxm is a pointer to a CxMessage corresponding to a real input
  648. event, and customcxobj is a pointer to the custom CxObject.  The
  649. custom function can extract the pointer to the input event by calling
  650. CxMsgData().  Before passing the CxMessage to the cu stom function,
  651. Commodities Exchange sets the CxMessage's ID to the ID passed to
  652. CxCustom().
  653.  
  654.  
  655. IX
  656.  
  657. Commodities Exchange does not use the input event description strings
  658. discussed earlier to match input events.  Instead, Commodities
  659. Exchange converts these strings to its own internal format.  These
  660. input expressions are available for commodities to use instead of the
  661. input description strings.
  662.  
  663. The following is the IX structure as defined in
  664. <libraries/commodities.h>:
  665.  
  666. #define IX_VERSION   2
  667.  
  668. struct InputXpression {
  669.    UBYTE   ix_Version;     /* must be set to IX_VERSION  */
  670.    UBYTE   ix_Class;       /* class must match exactly   */
  671.    UWORD   ix_Code;
  672.    UWORD   ix_CodeMask;    /* normally used for UPCODE   */
  673.    UWORD   ix_Qualifier;
  674.    UWORD   ix_QualMask;
  675.    UWORD   ix_QualSame;    /* synonyms in qualifier      */
  676.    };
  677. typedef struct InputXpression IX;
  678.  
  679. The ix_Version field contains the current version number of the
  680. InputXpression structure.  The current version is defined as
  681. IX_VERSION.  The ix_Class field contains the IECLASS_ constant (as
  682. defined in <devices/inputevent.h>) of the particular class of i nput
  683. event sought.  Commodities Exchange uses the ix_Code and ix_CodeMask
  684. fields to match the ie_Code field of a struct InputEvent.  The bits of
  685. ix_CodeMask indicate which bits are relevant in the ix_Code field when
  686. trying to match against a ie_Code.  If any bits in ix_CodeMask are
  687. off, Commodities Exchange does not consider the corresponding bit in
  688. ie_Code when trying to match input events.  This is used primarily to
  689. mask out the IECODE_UP_PREFIX bit of rawkey events, making it easier
  690. to match both up an d down presses of a particular key.
  691.  
  692. IX's qualifier fields, ix_Qualifier, ix_QualMask, and ix_QualSame, are
  693. used to match the ie_Qualifier field of an InputEvent structure.  The
  694. ix_Qualifier and ix_QualMask fields work just like ix_Code and
  695. ix_CodeMask.  The bits of ix_CodeMask indicate whic h bits are
  696. relevant when comparing ix_Qualifier to ie_Qualifier.  The ix_QualSame
  697. field tells Commodities Exchange that certain qualifiers are
  698. equivalent.  The bits of this field can be set to any of the following
  699. values:
  700.  
  701. #define IXSYM_SHIFT  1     /* left- and right- shift are equivalent     */
  702. #define IXSYM_CAPS   2     /* either shift or caps lock are equivalent  */
  703. #define IXSYM_ALT    4     /* left- and right- alt are equivalent       */
  704.  
  705.  
  706. For example, the input description string
  707.  
  708.  ``rawkey -caps -lalt -relativemouse -upstroke ralt tab''
  709.  
  710. matches a tab upstroke or downstroke with the right alt key pressed
  711. whether or not the left alt, either shift, or the capslock keys are
  712. down.  The following IX structure corresponds to that input
  713. description string:
  714.  
  715. IX ix = {
  716.     IX_VERSION,             /* The version */
  717.     IECLASS_RAWKEY,         /* We're looking for a RAWKEY event */
  718.     0x42,                   /* The key the usa0 keymap maps to a tab */
  719.     0x00FF & (~IECODE_UP_PREFIX),     /* We want up and down key presses */
  720.     IEQUALIFIER_RALT,                 /* The right alt key must be down */
  721.     0xFFFF & ~(IEQUALIFIER_LALT | IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT |
  722.                 IEQUALIFIER_CAPSLOCK | IEQUALIFIER_RELATIVEMOUSE),
  723.                 /* don't care about left alt, shift, capslock,
  724.                    or relativemouse qualifiers */
  725.     IXSYM_CAPS  /* The shift keys and the capslock
  726.                    key qualifers are all equivalent */
  727. };
  728.  
  729. The CxFilter() macro only accepts a description string to describe an
  730. input event.  A commodity can change this filter, however.
  731.  
  732. void SetFilter( CxObj *filter, char *descrstring );
  733. void SetFilterIX( CxObj *filter, IX *ix );
  734.  
  735. SetFilter() and SetFilterIX() change which input events a filter
  736. CxObject diverts.  SetFilter() accepts a pointer to an input
  737. description string.  SetFilterIX() accepts a pointer to an IX input
  738. expression.  A commodity that uses either of these functions should
  739. check the filter's error code with CxObjError() to make sure the
  740. change worked.
  741.  
  742. errorcode = LONG ParseIX( char *descrstring, IX *ix );
  743.  
  744. The function ParseIX() parses an input description string and
  745. translates it into an IX input expression.  Commodities Exchange uses
  746. ParseIX() to convert the description string in CxFilter() to an IX
  747. input expression.  As was mentioned previously, as of co
  748. mmodities.library version 37.3, ParseIX() does not work with certain
  749. kinds of input strings.
  750.  
  751. This article is by no means an exhaustive description of Commodities
  752. Exchange.  For more information, see the Commodities Exchange Autodocs
  753. (commodities.doc for commodities.library and cx.doc for the macros and
  754. linker library functions) and the sample commodities from the 1990
  755. Atlanta DevCon disks.
  756.  
  757.  
  758. /* broker.c - Simple skeletal example of opening a broker
  759.  * compiled with SASC 5.10
  760.  * LC -b0 -cfist -v broker.c
  761.  * Blink FROM LIB:c.o,broker.o TO broker LIBRARY
  762.  * LIB:LC.lib,LIB:Amiga.lib
  763.  */
  764. #include <exec/libraries.h>
  765. #include <libraries/commodities.h>
  766. #include <dos/dos.h>
  767. #include <clib/exec_protos.h>
  768. #include <clib/alib_protos.h>
  769. #include <clib/alib_stdio_protos.h>
  770. #include <clib/alib_commodities_protos.h>
  771. #include <clib/commodities_protos.h>
  772.  
  773. #ifdef LATTICE
  774. int CXBRK(void) { return(0); }  /* Disable Lattice CTRL/C handling */
  775. int chkabort(void) { return(0); }
  776. #endif
  777.  
  778. struct Library *CxBase;
  779.  
  780. void main(void);
  781. LONG ProcessMsg(void);
  782.  
  783. CxObj *broker;
  784.  
  785. struct MsgPort *broker_mp;
  786.  
  787. struct NewBroker newbroker = {
  788.     NB_VERSION, /* nb_Version - Version of the NewBroker structure */
  789.     "AmigaMail broker", /* nb_Name - Name CX uses to identify this commodity */
  790.     "Broker",   /* nb_Title - Title of commodity that appears in CXExchange */
  791.     "A simple example of a broker",  /* nb_Descr - Description of the commodity */
  792.     0,          /* nb_Unique - Tells CX not to launch new commodity with same name */
  793.     0,          /* nb_Flags - Tells CX if this commodity has a window */
  794.     0,          /* nb_Pri - This commodity's priority */
  795.     0,          /* nb_Port - MsgPort CX talks to */
  796.     0           /* nb_ReservedChannel - reserved for later use */
  797. };
  798.  
  799. ULONG cxsigflag;
  800.  
  801. void main()
  802. {
  803.     /* Before bothering with anything else, open the library */
  804.     if (CxBase = OpenLibrary("commodities.library", 37L))
  805.     {
  806.         /* Commodities talks to a Commodities application through
  807.          * an Exec Message port, which the application provides
  808.          */
  809.         if (broker_mp = CreateMsgPort())
  810.         {
  811.             newbroker.nb_Port = broker_mp;
  812.  
  813.             /* The commodities.library function CxBroker() adds a
  814.              * broker to the master list.  It takes two arguments,
  815.              * a pointer to a NewBroker structure and a pointer to
  816.              * a LONG.  The NewBroker structure contains information
  817.              * to set up the broker.  If the second argument is not
  818.              * NULL, CxBroker will fill it in with an error code.
  819.              */
  820.             if (broker = CxBroker(&newbroker, NULL))
  821.             {
  822.                 cxsigflag = 1L << broker_mp->mp_SigBit;
  823.  
  824.                 /* After it's set up correctly, the broker
  825.                  * has to be activated
  826.                  */
  827.                 ActivateCxObj(broker, 1L);
  828.  
  829.                 /* the main processing loop */
  830.                 while (ProcessMsg());
  831.  
  832.                 /* It's time to clean up.  Start by removing
  833.                  * the broker from the Commodities master list.
  834.                  * The DeleteCxObjAll() function will take care
  835.                  * of removing a CxObject and all those connected
  836.                  * to it from the Commodities network
  837.                  */
  838.                 DeleteCxObj(broker);
  839.             }
  840.             DeleteMsgPort(broker_mp);
  841.         }
  842.         CloseLibrary(CxBase);
  843.     }
  844. }
  845.  
  846. LONG ProcessMsg(void)
  847. {
  848.     CxMsg *msg;
  849.  
  850.     ULONG sigrcvd, msgid, msgtype;
  851.     LONG returnvalue = 1L;
  852.  
  853.     /* wait for something to happen */
  854.     sigrcvd = Wait(SIGBREAKF_CTRL_C | cxsigflag);
  855.  
  856.     /* process any messages */
  857.     while(msg = (CxMsg *)GetMsg(broker_mp))
  858.     {
  859.         /* Extract necessary information from the CxMessage and return it */
  860.         msgid = CxMsgID(msg);
  861.         msgtype = CxMsgType(msg);
  862.         ReplyMsg((struct Message *)msg);
  863.  
  864.         switch(msgtype)
  865.         {
  866.             case CXM_IEVENT:
  867.                 /* Shouldn't get any of these in this example */
  868.                 break;
  869.             case CXM_COMMAND:
  870.                 /* Commodities has sent a command */
  871.                 printf("A command: ");
  872.                 switch(msgid)
  873.                 {
  874.                     case CXCMD_DISABLE:
  875.                         printf("CXCMD_DISABLE\n");
  876.                         /* The user clicked Commodities Exchange disable
  877.                          * gadget better disable
  878.                          */
  879.                         ActivateCxObj(broker, 0L);
  880.                         break;
  881.                     case CXCMD_ENABLE:
  882.                         /* user clicked enable gadget */
  883.                         printf("CXCMD_ENABLE\n");
  884.                         ActivateCxObj(broker, 1L);
  885.                         break;
  886.                     case CXCMD_KILL:
  887.                         /* user clicked kill gadget, better quit */
  888.                         printf("CXCMD_KILL\n");
  889.                         returnvalue = 0L;
  890.                         break;
  891.                 }
  892.                 break;
  893.             default:
  894.                 printf("Unknown msgtype\n");
  895.                 break;
  896.         }
  897.     }
  898.  
  899.     /* Test to see if user tried to break */
  900.     if (sigrcvd & SIGBREAKF_CTRL_C)
  901.     {
  902.         returnvalue = 0L;
  903.         printf("CTRL C signal break\n");
  904.     }
  905.     return(returnvalue);
  906. }
  907.  
  908.  
  909.  
  910.  
  911.  
  912.  
  913.  
  914. /* HotKey.c - Simple hot key commodity
  915.  * compiled with SASC 5.10
  916.  * LC -b0 -cfist -v hotkey.c
  917.  * Blink FROM LIB:c.o,hotkey.o TO hotkey LIBRARY LIB:LC.lib,LIB:Amiga.lib
  918.  */
  919. #include <exec/libraries.h>
  920. #include <libraries/commodities.h>
  921. #include <dos/dos.h>
  922. #include <clib/exec_protos.h>
  923. #include <clib/alib_protos.h>
  924. #include <clib/alib_stdio_protos.h>
  925. #include <clib/alib_commodities_protos.h>
  926. #include <clib/commodities_protos.h>
  927.  
  928. #ifdef LATTICE
  929. int CXBRK(void) { return(0); }  /* Disable Lattice CTRL/C handling */
  930. int chkabort(void) { return(0); }
  931. #endif
  932.  
  933. #define EVT_HOTKEY 1L
  934.  
  935. struct Library *CxBase, *IconBase;
  936.  
  937. void main(int, char **);
  938. LONG ProcessMsg(void);
  939.  
  940. struct MsgPort *broker_mp;
  941.  
  942. CxObj *broker, *filter, *sender, *translate;
  943.  
  944. struct NewBroker newbroker = {
  945.     NB_VERSION,
  946.     "AmigaMail HotKey",           /* string to identify this broker */
  947.     "A Simple HotKey",
  948.     "A simple hot key commodity",
  949.     NBU_UNIQUE | NBU_NOTIFY,      /* Don't want any new commodities
  950.                                    * starting with this name.  If someone
  951.                                    * tries it, let me know */
  952.     0,
  953.     0,
  954.     0,
  955.     0
  956. };
  957.  
  958.  
  959. ULONG cxsigflag;
  960.  
  961. void main(int argc, char **argv)
  962. {
  963.     char *hotkey, **ttypes;
  964.  
  965.     if (CxBase = OpenLibrary("commodities.library", 37L))
  966.     {
  967.         /* open the icon.library for the support library
  968.          * functions, ArgArrayInit() and ArgArrayDone()
  969.          */
  970.         if (IconBase = OpenLibrary("icon.library", 36L))
  971.         {
  972.             if (broker_mp = CreateMsgPort())
  973.             {
  974.                 newbroker.nb_Port = broker_mp;
  975.                 cxsigflag = 1L << broker_mp->mp_SigBit;
  976.  
  977.                 /* ArgArrayInit() is a support library function
  978.                  * (from the 2.0 version of amiga.lib) that makes it
  979.                  * easy to read arguments from either a CLI or from
  980.                  * the Workbench's ToolTypes.  Because it uses
  981.                  * icon.library, the library has to be open before
  982.                  * calling this function.  ArgArrayDone() cleans up
  983.                  * after this function.
  984.                  */
  985.                 ttypes = ArgArrayInit(argc, argv);
  986.  
  987.                 /* ArgInt() (also from amiga.lib) searches through the
  988.                  * array set up by ArgArrayInit() for a specific
  989.                  * ToolType.  If it finds one, it returns the numeric
  990.                  * value of the number that followed the ToolType
  991.                  * (i.e. CX_PRIORITY=7).  If it doesn't find the ToolType,
  992.                  * it returns the default value (the third argument)
  993.                  */
  994.                 newbroker.nb_Pri = (BYTE)ArgInt(ttypes, "CX_PRIORITY", 0);
  995.  
  996.                 /* ArgString() works just like ArgInt(), except it
  997.                  * returns a pointer to a string rather than an integer.
  998.                  * In the example below, if there is no ToolType "HOTKEY",
  999.                  * the function returns a pointer to "rawkey control esc".
  1000.                  */
  1001.                 hotkey = ArgString(ttypes, "HOTKEY", "rawkey control esc");
  1002.  
  1003.                 if (broker = CxBroker(&newbroker, NULL))
  1004.                 {
  1005.                     /* CxFilter() is a macro that creates a filter
  1006.                      * CxObject.  This filter passes input events that
  1007.                      * match the string pointed to by hotkey.
  1008.                      */
  1009.                     if (filter = CxFilter(hotkey))
  1010.                     {
  1011.                         /* Add a CxObject to another's personal list */
  1012.                         AttachCxObj(broker, filter);
  1013.  
  1014.                         /* CxSender() creates a sender CxObject.  Every
  1015.                          * time a sender gets a CxMessage, it sends a new
  1016.                          * CxMessage to the port pointed to in the first
  1017.                          * argument.  CxSender()'s second argument will be
  1018.                          * the ID of any CxMessages the sender sends to
  1019.                          * the port.  The data pointer associated with the
  1020.                          * CxMessage will point to a *COPY* of the
  1021.                          * InputEvent structure associated with the orginal
  1022.                          * CxMessage.
  1023.                          */
  1024.                         if (sender = CxSender(broker_mp, EVT_HOTKEY))
  1025.                         {
  1026.                             AttachCxObj(filter, sender);
  1027.  
  1028.                             /* CxTranslate() creates a translate CxObject.
  1029.                              * When a translate CxObject gets a CxMessage,
  1030.                              * it deletes the original CxMessage and adds
  1031.                              * a new input event to the input.device's
  1032.                              * input stream after the Commodities input
  1033.                              * handler.  CxTranslate's argument points
  1034.                              * to an InputEvent structure from which to
  1035.                              * create the new input event.  In this example,
  1036.                              * the pointer is NULL, meaning no new event
  1037.                              * should be introduced.
  1038.                              */
  1039.                             if (translate = (CxTranslate(NULL)))
  1040.                             {
  1041.                                 AttachCxObj(filter, translate);
  1042.  
  1043.                                 /* CxObjError() is a commodities.library
  1044.                                  * function that returns the internal
  1045.                                  * accumulated error code of a CxObject.
  1046.                                  */
  1047.                                 if (! CxObjError(filter))
  1048.                                 {
  1049.                                     ActivateCxObj(broker, 1L);
  1050.                                     while (ProcessMsg());
  1051.                                 }
  1052.                             }
  1053.                         }
  1054.                     }
  1055.                     /* DeleteCxObjAll() is a commodities.library function
  1056.                      * that not only deletes the CxObject pointed to in
  1057.                      * its argument, but it deletes all of the CxObjects
  1058.                      * that are attached to it.
  1059.                      */
  1060.                     DeleteCxObjAll(broker);
  1061.                 }
  1062.                 DeleteMsgPort(broker_mp);
  1063.             }
  1064.  
  1065.             /* this amiga.lib function cleans up after ArgArrayInit() */
  1066.             ArgArrayDone();
  1067.             CloseLibrary(IconBase);
  1068.         }
  1069.         CloseLibrary(CxBase);
  1070.     }
  1071. }
  1072.  
  1073.  
  1074.  
  1075.  
  1076.  
  1077. LONG ProcessMsg(void)
  1078. {
  1079.     extern struct MsgPort *broker_mp;
  1080.     extern CxObj *broker;
  1081.  
  1082.     extern ULONG cxsigflag;
  1083.  
  1084.     CxMsg *msg;
  1085.  
  1086.     ULONG sigrcvd, msgid, msgtype;
  1087.     LONG returnvalue = 1L;
  1088.  
  1089.     sigrcvd = Wait(SIGBREAKF_CTRL_C | cxsigflag);
  1090.  
  1091.     while(msg = (CxMsg *)GetMsg(broker_mp))
  1092.     {
  1093.         msgid = CxMsgID(msg);
  1094.         msgtype = CxMsgType(msg);
  1095.         ReplyMsg((struct Message *)msg);
  1096.  
  1097.         switch(msgtype)
  1098.         {
  1099.             case CXM_IEVENT:
  1100.                 printf("A CXM_EVENT, ");
  1101.                 switch(msgid)
  1102.                 {
  1103.                     case EVT_HOTKEY:
  1104.                     /* We got the message from the sender CxObject */
  1105.                         printf("You hit the HotKey.\n");
  1106.                         break;
  1107.  
  1108.                     default:
  1109.                         printf("unknown.\n");
  1110.                         break;
  1111.                 }
  1112.                 break;
  1113.  
  1114.             case CXM_COMMAND:
  1115.                 printf("A command: ");
  1116.                 switch(msgid)
  1117.                 {
  1118.                     case CXCMD_DISABLE:
  1119.                         printf("CXCMD_DISABLE\n");
  1120.                         ActivateCxObj(broker, 0L);
  1121.                         break;
  1122.  
  1123.                     case CXCMD_ENABLE:
  1124.                         printf("CXCMD_ENABLE\n");
  1125.                         ActivateCxObj(broker, 1L);
  1126.                         break;
  1127.  
  1128.                     case CXCMD_KILL:
  1129.                         printf("CXCMD_KILL\n");
  1130.                         returnvalue = 0L;
  1131.                         break;
  1132.  
  1133.                     case CXCMD_UNIQUE:
  1134.                     /* Commodities Exchange can be told not
  1135.                      * only to refuse to launch a commodity with
  1136.                      * a name already in use but also can notify
  1137.                      * the already running commodity that it happened.
  1138.                      * It does this by sending a CXM_COMMAND with the
  1139.                      * ID set to CXMCMD_UNIQUE.  If the user tries
  1140.                      * to run a windowless commodity that is already
  1141.                      * running, the user wants the commodity to shut down.
  1142.                      */
  1143.                         printf("CXCMD_UNIQUE\n");
  1144.                         returnvalue = 0L;
  1145.                         break;
  1146.                     default:
  1147.                         printf("Unknown msgid\n");
  1148.                         break;
  1149.                 }
  1150.                 break;
  1151.             default:
  1152.                 printf("Unknown msgtype\n");
  1153.                 break;
  1154.         }
  1155.     }
  1156.  
  1157.  
  1158.     if (sigrcvd & SIGBREAKF_CTRL_C)
  1159.     {
  1160.         returnvalue = 0L;
  1161.         printf("CTRL C signal break\n");
  1162.     }
  1163.  
  1164.     return(returnvalue);
  1165. }
  1166.  
  1167.